package Pythagonatsystem;

// Imports.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.Timer;
import java.util.ArrayList;

// Vores panel som i virkligheden er en Jframe med et Jpanel indeni defineres her som en subklasse til Jframe.
// PythagonatPanel klasse oprettes som en sub af et standard JPanel.
public class PythagonatApp extends JFrame {

    // PythagonatApp klassens kontruktør som opretter en ny instans af PythagonatPanel klassen. PythagonatPanel's konstruktør sørger for resten af opsætningen.
    public PythagonatApp() {
        PhytagonatPanel panel = new PhytagonatPanel();
        add(panel); // Panelet tilføjes vores Jframe PythagonatApp.

    }

    // main metode, opretter og viser vores JFrame
    public static void main(String[] args) {
        PythagonatApp panel = new PythagonatApp();
        panel.showUI();
    }

    // Metode til at vise vores JFrame oprettes.
    public void showUI() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Sæt "lukke handling"
        setTitle("Pythagonatsystem"); // Vindue titel.
        setSize(465, 500); // Vindue størrelse.
        setVisible(true); // Vinduets synlighed.
    }
}

class PhytagonatPanel extends JPanel {

    // X-akse Koordinat konstanter defineres
    public static final int X_AXIS_FIRST_X_COORD = 50;
    public static final int X_AXIS_SECOND_X_COORD = 400;
    public static final int X_AXIS_Y_COORD = 400;
    // Y-akse Koordinat konstanter defineres
    public static final int Y_AXIS_FIRST_Y_COORD = 50;
    public static final int Y_AXIS_SECOND_Y_COORD = 400;
    public static final int Y_AXIS_X_COORD = 50;
    // Længde konstanter til "pilene" i enden af X og Y aksen.
    public static final int FIRST_LENGHT = 10;
    public static final int SECOND_LENGHT = 5;
    // Distance konstant som definerer længden fra aksernes "strenge" til aksen.
    public static final int AXIS_STRING_DISTANCE = 20;
    
    // Nummerer akser (antal X og Y koordinater ønsket defineres), samt X og Y længden (den individuelle koordinat længde) udregnes
    public static final int xCoordNumbers = 11;
    public static final int yCoordNumbers = 11;
    public static final int xLength = (X_AXIS_SECOND_X_COORD - X_AXIS_FIRST_X_COORD) / xCoordNumbers;
    public static final int yLength = (Y_AXIS_SECOND_Y_COORD - Y_AXIS_FIRST_Y_COORD) / yCoordNumbers;
    
    // Vores nulpunkt på X og Y aksen fastlægges.
    public static final int X_ORIGIN = X_AXIS_FIRST_X_COORD + xLength;
    public static final int Y_ORIGIN = Y_AXIS_SECOND_Y_COORD - yLength;
    
    // Privat ArrayListe af vores Line klasse initialiseres til at holde vores linier som skal tegnes på pythagonatsystemet.
    private ArrayList<Line> lineList = new ArrayList<Line>();
    
    // GUI elementer initialiseres
    JLabel labelText = new JLabel(); // tekstlabel "indtast længden på de 2 linier..."
    JLabel labelReply = new JLabel(); // svarlabel som giver svaret til sidst, eller en fejlmeddelelse.
    JTextField textFieldLine1 = new JTextField(); // linie felt 1
    JTextField textFieldLine2 = new JTextField(); // linie felt 2
    JButton buttonCalc = new JButton(); // Udregn knappen
    Timer timer = new Timer(0, new java.awt.event.ActionListener() { // timer oprettes med dertilhørede actionevent.

        public void actionPerformed(ActionEvent e) {
            lineDraw_actionPerformed(e); // lineDraw metoden kaldes.
        }
    });
    
    // nedenfor initialiseres variablerne til at holde linieværdier og de midlertidige værdier når linierne skal tegnes på panelet.
    public double hold1, hold2, var1, var2;

    // Klassens konstruktør som opsætter alle vores grafiske elementer og knapper.
    public PhytagonatPanel() {
        labelText.setText("Indtast længden på de 2 linier og tryk 'Udregn'"); // teks på labelText tekstfeltet.
        labelText.setBounds(new Rectangle(140, 50, 300, 15)); // absolut placering

        labelReply.setBounds(new Rectangle(140, 155, 300, 15)); // absolut placering

        textFieldLine1.setText("3"); // tekst på linie felt 1
        textFieldLine1.setBounds(new Rectangle(240, 80, 100, 29)); // absolut placering

        textFieldLine2.setText("4"); // tekst på linie felt 2
        textFieldLine2.setBounds(new Rectangle(240, 115, 100, 29)); // absolut placering

        buttonCalc.setText("Udregn"); // tekst på knappen
        buttonCalc.setMnemonic(KeyEvent.VK_U); // tastatur genvej til knappen Alt+U
        buttonCalc.setBounds(new Rectangle(140, 80, 91, 32)); // absolut placering
        buttonCalc.addActionListener(new java.awt.event.ActionListener() { // actionlistener som lytter efter tryk på knappen

            public void actionPerformed(ActionEvent e) {
                buttonCalc_actionPerformed(e); // buttonCalc metoden kaldes ved tryk action.
            }
        });

        this.setLayout(null); // sætter Layout til null da vi ikke gør brug af LayoutManager men absolute positioning.

        // elementerne tilføjes til panelet her refereret med "this"
        this.add(labelText);
        this.add(labelReply);
        this.add(buttonCalc);
        this.add(textFieldLine1);
        this.add(textFieldLine2);
    }

    // lineDraw metode som bliver kaldt for hvert timer tick når swing timeren er startet.
    // metoden tegner vores linier i trekanten pixel for pixel.
    void lineDraw_actionPerformed(ActionEvent e) {
        lineList.get(0).getB().setX(var1); // sætter vores l1 og l2 liniers punkt B X og Y punkter til værdien i var 1 og 2
        lineList.get(1).getB().setY(var2); //
        if (var1 >= hold1) { lineList.get(0).setDrawn(true); } // sætter en linie til at være "tegnet" hvis dens længde nu er lig den ønskede.
        if (var2 >= hold2) { lineList.get(1).setDrawn(true); } //
        if ( !lineList.get(0).isDrawn() ) { var1 += 1; } // tegner den første linie hvis den ikke allerede er tegnet.
        if ( !lineList.get(1).isDrawn() && lineList.get(0).isDrawn() ) { var2 += 1; } // tegner først anden linie når første linie er tegnet.
        // timeren stoppes linie variabler var1,2 hold1,2 nulstillet med reset metoden og vores hypotenuselængde udregnes med liniens lenght metode divideret med
        // xLenght for at få længden i forhold til vores koordinatsystem og ikke Jpanelets pixels.
        if (lineList.get(0).isDrawn() && lineList.get(1).isDrawn()) { timer.stop(); reset(); labelReply.setText("Længden på hypotenusen er: " + (lineList.get(2).lenght())/xLength); }
        repaint(); // panelet gentegnes.
    }

    // knap tryk metode, kaldes ved knaptryk event på buttonCalc.
    void buttonCalc_actionPerformed(ActionEvent e) {
        labelReply.setText(""); // rydder vores Reply tekstfelt.
        if (isDouble(textFieldLine1.getText()) && isDouble(textFieldLine2.getText())) { // kontroller at begge inputs er doubles.
            Line l1 = new Line(new Point(0,0),new Point(Double.parseDouble(textFieldLine1.getText())*xLength, 0)); // opretter første linie
            Line l2 = new Line(new Point(0,0),new Point(0, Double.parseDouble(textFieldLine2.getText())*xLength)); // opretter anden linie 
            Line l3 = new Line(l1.getB(),l2.getB()); // opretter hypotenusen, med de 2 andre liniers B punkter.
            lineList.clear(); // rydder listen for evt. tidligere linier.
            lineList.add(l1); // tilføjer de nye linier til listen.
            lineList.add(l2); //
            lineList.add(l3); //
            hold1 = lineList.get(0).getB().getX(); // sætter vores hold1 og hold2 til værdierne fra linie 1 og linie2 så vi kan manupulere med længden på linierne når de skal tegnes.
            hold2 = lineList.get(1).getB().getY(); //
            timer.start(); // starter vores timer, som samtidig animerer vores linier.
        } else {
            labelReply.setText("Indtast venligst kun tal!"); // svar besked hvis der ikke inputtes doubles.
        }
    }

    // metode til at kontrollere om en input string er en double, virker ved at forsøge at parse inputtet som en double, lykkedes det returneres der sandt, eller falsk.
    public boolean isDouble(String input) {
        try {
            Double.parseDouble(input); // parser inputtet
            return true; // returner true
        } catch (Exception e) {
            return false; // returner falsk
        }
    }

    // Vi overrider her JPanelets standard paintComponent methode, så vi kan tegne som vi vil.
    @Override
    public void paintComponent(Graphics g) {

        // Her kaldes selv samme metode men fra superklassen, for at tegne en "blank" baggrund
        super.paintComponent(g);

        // vores grafikobjekt g er i virkligheden et 2D grafikobjekt (Java 2 og fremefter) og castes her om for at tilføje yderligere metoder til bla. aliasing.
        Graphics2D g2 = (Graphics2D) g;

        // RenderingHints enables for at sikre en pænere og renere tekst (antialiasing)
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        // Tegn X-aksen
        g2.drawLine(X_AXIS_FIRST_X_COORD, X_AXIS_Y_COORD, X_AXIS_SECOND_X_COORD, X_AXIS_Y_COORD);
        
        // Tegn Y-aksen
        g2.drawLine(Y_AXIS_X_COORD, Y_AXIS_FIRST_Y_COORD, Y_AXIS_X_COORD, Y_AXIS_SECOND_Y_COORD);

        // Tegn X-aksens pil ( 2 kateter fra en trekant )
        g2.drawLine(X_AXIS_SECOND_X_COORD - FIRST_LENGHT, X_AXIS_Y_COORD - SECOND_LENGHT, X_AXIS_SECOND_X_COORD, X_AXIS_Y_COORD);
        g2.drawLine(X_AXIS_SECOND_X_COORD - FIRST_LENGHT, X_AXIS_Y_COORD + SECOND_LENGHT, X_AXIS_SECOND_X_COORD, X_AXIS_Y_COORD);

        // Tegn Y-aksens pil ( 2 kateter fra en trekant )
        g2.drawLine(Y_AXIS_X_COORD - SECOND_LENGHT, Y_AXIS_FIRST_Y_COORD + FIRST_LENGHT, Y_AXIS_X_COORD, Y_AXIS_FIRST_Y_COORD);
        g2.drawLine(Y_AXIS_X_COORD + SECOND_LENGHT, Y_AXIS_FIRST_Y_COORD + FIRST_LENGHT, Y_AXIS_X_COORD, Y_AXIS_FIRST_Y_COORD);

        // Tegn "X" og "Y" teksten i på de 2 akser.
        g2.drawString("X", X_AXIS_SECOND_X_COORD - AXIS_STRING_DISTANCE / 2, X_AXIS_Y_COORD + AXIS_STRING_DISTANCE);
        g2.drawString("Y", Y_AXIS_X_COORD - AXIS_STRING_DISTANCE, Y_AXIS_FIRST_Y_COORD + AXIS_STRING_DISTANCE / 2);

        // Tegn tal og streger på X-aksen
        for (int i = 1; i < xCoordNumbers; i++) {
            g2.drawLine(X_AXIS_FIRST_X_COORD + (i * xLength), X_AXIS_Y_COORD - SECOND_LENGHT, X_AXIS_FIRST_X_COORD + (i * xLength), X_AXIS_Y_COORD + SECOND_LENGHT);
            g2.drawString(Integer.toString(i - 1), X_AXIS_FIRST_X_COORD + (i * xLength) - 3, X_AXIS_Y_COORD + AXIS_STRING_DISTANCE);
        }

        // Tegn tal og streger på Y-aksen
        for (int i = 1; i < yCoordNumbers; i++) {
            g2.drawLine(Y_AXIS_X_COORD - SECOND_LENGHT, Y_AXIS_SECOND_Y_COORD - (i * yLength), Y_AXIS_X_COORD + SECOND_LENGHT, Y_AXIS_SECOND_Y_COORD - (i * yLength));
            g2.drawString(Integer.toString(i - 1), Y_AXIS_X_COORD - AXIS_STRING_DISTANCE, Y_AXIS_SECOND_Y_COORD - (i * yLength) + 3);
        }
        
        // for hver linie i vores linieliste oprettes fire ints udfra vores punkter i linien.
        // disse ints lægges så henholdsvis til og trækkes fra X og Y udganspunktet i pythagonatsystemet og ganges med X og Y længden
        // for at de passer med intervallerne i pythagonatsystemet.
        for (Line l : lineList) {
            // linien oprettes som en instans af Line2D klassen.
            Line2D.Double line = new Line2D.Double(X_ORIGIN + l.getA().getX(), Y_ORIGIN - l.getA().getY(), X_ORIGIN + l.getB().getX(), Y_ORIGIN - l.getB().getY());
            g2.draw(line); // linien tegnes
        }
    }

    // reset metode, nulstiller de 4 variabler som jeg bruger til at tegne med på panelet.
    private void reset() {
        hold1 = 0; hold2 = 0; var1 = 0; var2 = 0;
    }
}